/*
 * Copyright (C) 2010, Funcom AS, All Rights Reserved
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  @authors
 *  Svein Gunnar Larsen <sveingunnarlarsen@gmail.com>
 *  Erik Thune Lund <culex.eriklund@gmail.com>
 *  Michel Krohn-Dale <kronicz@gmail.com>
 */

package com.funcom.peeler.peelable;

import com.funcom.peeler.PeelStyleSheet;
import com.funcom.peeler.ResourceLoader;
import com.funcom.peeler.component.PeelComponentFactory;
import com.funcom.peeler.symbol.Symbol;
import com.funcom.peeler.tool.Settings;
import com.funcom.peeler.utils.WorkBenchToolBar;
import com.jmex.bui.BComponent;
import com.jmex.bui.provider.ResourceProvider;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

public class PeelRoot {

    PeelWindow root_window;
    PeelStyleSheet stylesheet;
    String stylesheet_filename;
    String lastPath;
    DocumentBuilder builder;
    List<PeelComponent> children = new LinkedList<PeelComponent>();
    Map< BComponent, PeelComponent > componentMap = new HashMap< BComponent, PeelComponent >();
    ResourceProvider provider;
    private Settings settings;
    private WorkBenchToolBar toolbar;

    public PeelRoot( PeelWindow window , WorkBenchToolBar toolbar) {

        settings = new Settings( this );
        this.toolbar = toolbar;
        root_window = window;
        add( window );

        stylesheet = ResourceLoader.getInstance().getDefaultWorkBenchStyle();

        try {

            builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        }
        catch ( Exception e ) {
            System.out.println( e );
        }
    }

    public Document peel() {

        Document doc = builder.newDocument();

        Element root = doc.createElement( Symbol.Node.root );
        doc.appendChild( root );

        if ( getStyleSheetPath() != null ) {
            root.setAttribute( Symbol.Attr.stylesheet, getStyleSheetPath() );
        }

        for ( PeelComponent peelable : children) {
            Element peeled_element = peelable.peel( doc );

            if ( peeled_element != null ) root.appendChild( peeled_element );
        }

        peelSettings( root );

        return doc;
    }

     public void unpeel( Document doc ) {

        Element root = doc.getDocumentElement();

        if ( root.getTagName().equals( Symbol.Node.root ) ) {

            unpeelSettings( root );
            unpeelStyleSheet( root );

            if ( root_window instanceof PeelWorkSpace )
                ((PeelWorkSpace)root_window).getComponent().setStyleSheet( stylesheet );

            NodeList nodes = root.getChildNodes();
            for ( int i = 0 ; i < nodes.getLength() ; i++ ) {

                Node node = nodes.item( i );

                if ( node instanceof Element ) {

                    PeelComponent component = PeelComponentFactory.makePeelComponent( (Element)node );

                    if ( component != null )
                        component.unpeel( (Element)node );


                        if ( component instanceof PeelWorkSpace ) {
                            remove( root_window );
                            add( (PeelWindow)component );
                            root_window = (PeelWindow)component;
                            ((PeelWorkSpace)root_window).getComponent().setStyleSheet( stylesheet );
                            continue;
                        }

                        root_window.addChild( component );
                }
            }
        }
    }

    public List< PeelComponent > getChildren() {
        return children;
    }

    public synchronized Set< PeelComponent > getAllComponents() {
        return new HashSet<PeelComponent> (componentMap.values() );
    }

    public void add( PeelWindow child ) {
        children.add(child); // Direct children
        child.setRoot( this );
    }

    public synchronized  void addToMap( PeelComponent peelComponent ) {
        componentMap.put( peelComponent.getComponent(), peelComponent ); // Add it to the map
    }

    public synchronized void removeFromMap( PeelComponent component ) {
        if ( componentMap.containsKey( component.getComponent() ) ) componentMap.remove( component.getComponent() );
    }

    public synchronized void remove( PeelWindow child ) {

        children.remove(child);
        root_window.getComponent().remove( child.getComponent() );
        componentMap.remove( child.getComponent() );
    }

    public synchronized PeelComponent getPeelComponent( BComponent bComponent ) {
        return componentMap.get( bComponent );
    }

    public PeelStyleSheet getStyleSheet() {
        return stylesheet;
    }

    public PeelWindow getRootWindow() {
        return root_window;
    }

    public void setStyleSheetPath( String path ) {
        stylesheet_filename = path;
    }

    public String getStyleSheetPath() {
        return ( stylesheet_filename == null ) ?
                ResourceLoader.getInstance().getStyleSheetPath( stylesheet ) :
                stylesheet_filename;
    }

    protected void unpeelStyleSheet( Element root ) {

        Document doc = root.getOwnerDocument();

        if ( root.hasAttribute( Symbol.Attr.stylesheet ) ) {

            try {
                URI uri = new URI( doc.getDocumentURI() );
                String path_to_stylesheet = uri.resolve( "." ).getPath() + root.getAttribute( Symbol.Attr.stylesheet );
                System.out.println(  path_to_stylesheet  );
                stylesheet = ResourceLoader.getInstance().loadStyleSheet( path_to_stylesheet );
                stylesheet_filename = root.getAttribute( Symbol.Attr.stylesheet );
            } catch ( URISyntaxException e ) {
                System.out.println( "Invalid URI syntax: " + e.getMessage() );
                e.printStackTrace();
            }
        }
    }

    public Settings getSettings() {
            return settings;
    }
    public WorkBenchToolBar getToolBar(){
        return toolbar;
    }
    protected void peelSettings( Element element ) {

        if ( lastPath != null )
            element.setAttribute( Symbol.Attr.last_path, lastPath );

        if ( settings.getLocalizerPath() != null )
            element.setAttribute( Symbol.Attr.localizer_path, settings.getLocalizerPath() );

        element.setAttribute( Symbol.Attr.base_path, settings.getBasePath() );

        if ( toolbar.gridEnabled() )
            element.setAttribute( Symbol.Setting.grid_toggle, Symbol.Toggle.enabled );
        else
            element.setAttribute( Symbol.Setting.grid_toggle, Symbol.Toggle.disabled );

        element.setAttribute( Symbol.Setting.grid_size, Integer.toString( toolbar.getGridStep() ) );
    }

    protected void unpeelSettings( Element element ) {

        if ( element.hasAttribute( Symbol.Attr.last_path ) )
            lastPath = element.getAttribute( Symbol.Attr.last_path );

        if ( element.hasAttribute( Symbol.Attr.localizer_path ) )
            settings.setLocalizerPath( element.getAttribute( Symbol.Attr.localizer_path ) );

        if ( element.hasAttribute( Symbol.Attr.base_path ) )
            settings.setBasePath( element.getAttribute( Symbol.Attr.base_path ) );

        if ( element.hasAttribute( Symbol.Setting.grid_toggle ) ) {

            if ( element.getAttribute( Symbol.Setting.grid_toggle ).equals( Symbol.Toggle.enabled ) )
                toolbar.setGridEnabled( true );
            else
                toolbar.setGridEnabled( false );
        }

        if ( element.hasAttribute( Symbol.Setting.grid_size ) ) {
            toolbar.setGridStep( Integer.valueOf( element.getAttribute( Symbol.Setting.grid_size ) ) );
        }
    }

    public void setLastPath( String path ) {
        lastPath = path;
    }

    public String getLastPath() {
        return lastPath;
    }
}
